﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json.Serialization;
using Sol_NetCoreDemo.Controllers.DI;
using Swashbuckle.AspNetCore.Swagger;

namespace Sol_NetCoreDemo
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        private string title = Assembly.GetExecutingAssembly().GetName().Name;

        /// <summary>
        /// ConfigureServices
        /// </summary>
        /// <param name="services">services</param>
        public void ConfigureServices(IServiceCollection services)
        {

            #region 集中注入服务
            //foreach (var item in GetClassName(Assembly.GetExecutingAssembly().GetName().Name))
            //{
            //    foreach (var typeArray in item.Value)
            //    {
            //        services.AddScoped(typeArray, item.Key);
            //    }
            //}
            #endregion

            #region 注入

            //Singleton: 单例模式，服务在第一次请求时被创建，其后的每次请求都沿用这个已创建的服务。我们不用再自己写单例了。
            //Transient: 瞬态模式，服务在每次请求时被创建，它最好被用于轻量级无状态服务。
            //Scoped: 作用域模式：
            //描述1：一次完整请求（比如Web页面的一次请求）内对象实例是相同的，但每次请求会产生一个新实例
            //描述1：（服务在每次请求时被创建，整个请求过程中都贯穿使用这个创建的服务。比如Web页面的一次请求）。

            services.AddSingleton<IWork, Work>();//注入
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

            services.AddWorkService(Configuration.GetConnectionString("Redis"));////注入，添加值（另外一种方式）
            #endregion

            #region 添加 jwt 认证服务
            services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = "JwtBearer";
                options.DefaultChallengeScheme = "JwtBearer";
            })
            .AddJwtBearer("JwtBearer", jwtBearerOptions =>
            {
                jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    //这个码可以随便填写，但是要和 TokenJWT 实体类中的码 对应
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("!my secret key is ULT72!ULT72!ULT72!ULT72!ULT72!")),

                    ValidateIssuer = true,
                    ValidIssuer = "TokenServer",

                    ValidateAudience = true,
                    ValidAudience = "audience",

                    //ValidateLifetime = true, //validate the expiration and not before values in the token

                    //ClockSkew = TimeSpan.FromMinutes(5) //5 minute tolerance for the expiration date
                };
            });
            #endregion

            #region Swagger
            //备注：需要在项目 属性-生成-XML文档文件  打钩，不然会报错
            //访问路径：http://localhost:5000/swagger/index.html   或者 http://localhost:5000/swagger
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("V1", new Info
                {
                    Version = "V1",
                    Title = title
                });
                c.ResolveConflictingActions(apiDescriptions => apiDescriptions.FirstOrDefault());
                c.CustomSchemaIds(x => x.FullName);
                var basePath = Microsoft.Extensions.PlatformAbstractions.PlatformServices.Default.Application.ApplicationBasePath;
                //var basePath = Directory.GetCurrentDirectory();
                var xmlPath = Path.Combine(basePath, $"{title}.xml");
                c.IncludeXmlComments(xmlPath);
            });
            #endregion

            //services.AddDbContext<EFCoreContext>();  EF


            //不加这句话，API返回值的键值对的键全部小写
            services.AddMvc().AddJsonOptions(options => { options.SerializerSettings.ContractResolver = new DefaultContractResolver(); });



            services.AddCors();//跨域
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        }

        /// <summary>
        /// Configure
        /// </summary>
        /// <param name="app">app</param>
        /// <param name="env">env</param>
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseHsts();
                app.UseExceptionHandler();
            }

            //跨域      
            app.UseCors(builder =>
            {
                builder.AllowAnyOrigin();
                builder.AllowAnyMethod();
                builder.AllowAnyHeader();
                builder.AllowCredentials();
                //app.UseCors(builder => builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader().AllowCredentials());
            });

            app.UseStaticFiles();//访问wwwroot下的文件，不添加会报404            
            app.UseAuthentication();//添加 认证 MiddleWare

            //Swagger
            app.UseSwagger();
            app.UseSwaggerUI(c =>
            {
                //c.SwaggerEndpoint($"/API/V1/{title}.json", title);
                c.SwaggerEndpoint($"/swagger/V1/swagger.json", title);
            });

            app.UseHttpsRedirection();//http变成https

            app.UseMvc();//一般放至最后
        }

        #region 获取程序集中的实现类对应的多个接口       
        /// <summary>  
        /// 获取程序集中的实现类对应的多个接口
        /// </summary>  
        /// <param name="assemblyName">程序集</param>
        public Dictionary<Type, Type[]> GetClassName(string assemblyName)
        {
            if (!String.IsNullOrEmpty(assemblyName))
            {
                Assembly assembly = Assembly.Load(assemblyName);
                List<Type> ts = assembly.GetTypes().ToList();

                var result = new Dictionary<Type, Type[]>();
                foreach (var item in ts.Where(s => !s.IsInterface))
                {
                    var interfaceType = item.GetInterfaces();
                    result.Add(item, interfaceType);
                }
                return result;
            }
            return new Dictionary<Type, Type[]>();
        }
        #endregion
    }
}
